// Application implementation for Gtk+
// Copyright 2006 (C) Ralph Thomas

#include <toolkit/application.h>
#include <util/resources.h>
#include <vector>
#include <algorithm>
#include <boost/thread/recursive_mutex.hpp>
#include <gtk/gtk.h>
#include <iostream>
#include <stdexcept>
#include <binreloc.h>

namespace bfs = boost::filesystem;

namespace toolkit {
	class applicationImpl : public application {
		typedef std::vector<thread_function_t>		threadFuncList;
		typedef boost::recursive_mutex::scoped_lock	lock;
		typedef boost::recursive_mutex			mutex;
		
		threadFuncList	_functions;
		mutex		_mutex;
		GtkIconFactory*	_stockIcons;
		//
		/// Gtk+ idle entry point.
		//
		static gboolean on_idle(gpointer data) {
			applicationImpl* impl = static_cast<applicationImpl*>(data);
			if (!impl) return false;
			impl->callIdlers();
			return false;
		}
		//
		/// This function will call all of the idle functions.
		//
		void callIdlers() {
			//
			// Call of the thread idle functions.
			//
			threadFuncList safeFuncs;
			{
				lock lk( _mutex );
				safeFuncs = _functions;
				_functions.clear();
			}
			threadFuncList::iterator first( safeFuncs.begin() );
			threadFuncList::iterator last( safeFuncs.end() );
			while( first != last ) {
				try { (*first)(); }
				catch( std::exception& ex ) { std::cerr << "idle time exception: " << ex.what() << std::endl; }
				catch( ... ) { std::cerr << "unknown idle exception!" << std::endl; }
				first++;
			}
		}
	  public:
		applicationImpl() : _stockIcons( 0 ) {}
		virtual ~applicationImpl() {
			if( _stockIcons ) {
				gtk_icon_factory_remove_default( _stockIcons );
				g_object_unref( _stockIcons );
			}
		}
		virtual initReturn init() {
			//
			// Initialize BinReloc so that we can correctly
			// load resource files.
			//
			BrInitError error;
			br_init( &error );
			bfs::path resource( util::getResourcePath() );
			std::string gtkrc_path( bfs::path( resource / "gtkrc" ).native_file_string() );
			//
			// Load our stock items. The gtkrc file defines which icons to use for
			// these.
			//
			GtkStockItem item;
			item.stock_id = "rotate_cw";
			item.label = "";
			item.modifier = (GdkModifierType) 0;
			item.keyval = 0;
			item.translation_domain = "";
			gtk_stock_add( &item, 1 );
			item.stock_id = "rotate_ccw";
			gtk_stock_add( &item, 1 );
			//
			// Load the custom gtkrc file.
			//
			gtk_rc_parse( gtkrc_path.c_str() );
			return kOk;
		}
		virtual void run() { gtk_main(); }
		virtual void quit() { gtk_main_quit(); }
		virtual void doIdle() { callIdlers(); }
		virtual void executeInMain( const thread_function_t& f ) {
			lock lk( _mutex );
			_functions.push_back( f );
			//
			// The glib main loop functions are all threadsafe, so
			// it's perfectly okay to do this.
			//
			g_idle_add( &applicationImpl::on_idle, this );
		}
	};
	application& application::getInstance() {
		static applicationImpl app;
		return app;
	}
	application::~application() {}
}

